﻿using Microsoft.VisualStudio.Debugger.Interop;

namespace Red.Scripts.Debugger.Remote.Debugger.Breakpoint
{
    public class Location
    {
        #region Private Data

        private uint _line;

        #endregion Private Data

        #region Public Properties

        public string File { get; private set; }
        public uint Column { get; private set; }
        public uint Length { get; private set; }
        public uint Position { get; private set; }

        public string RelativePath => ConvertToRelativePath();
        public uint EndColumn => Column + Length;

        /// <summary>
        /// Counts from zero
        /// </summary>
        public uint RedEngineLine
        {
            get { return _line; }
            private set { _line = value; }
        }

        /// <summary>
        /// Counts from one
        /// </summary>
        public uint VisualStudioLine
        {
            get { return _line; }
            private set { _line = value; }
        }

        #endregion Public Properties

        #region C-Tor

        private Location()
        {
        }

        #endregion C-Tor

        #region Private Methods

        // Hack - Need to figure out the correct way to convert an absolute path to a relative script-path
        private string ConvertToRelativePath()
        {
            int index = File.IndexOf( "scripts" );

            if ( index != -1 )
                return File.Substring( index + "scripts".Length + 1 );

            return File;
        }

        private bool BaseCompare( Location other )
        {
            return RelativePath == other.RelativePath && RedEngineLine == other.RedEngineLine;
        }

        #endregion Private Methods

        #region Public Methods

        public void CopyTo( comm.ScriptBreakpointAbsolute breakpoint )
        {
            breakpoint.standard.file = RelativePath;
            breakpoint.standard.line = (int)RedEngineLine;

            breakpoint.position = Position;
        }

        public void CopyTo( comm.ScriptBreakpointRelative breakpoint )
        {
            breakpoint.standard.file = RelativePath;
            breakpoint.standard.line = (int)RedEngineLine;

            breakpoint.column = (ushort)Column;
            breakpoint.length = (ushort)Length;
        }

        public bool CompareAbsolute( Location other )
        {
            return BaseCompare( other ) && Position == other.Position;
        }

        public bool CompareRelative( Location other )
        {
            return BaseCompare( other ) && Column == other.Column && Length == other.Length;
        }

        #endregion Public Methods

        #region Public Functions

        public static Location CreateFromRedEngine( comm.ScriptBreakpointAbsolute breakpoint )
        {
            Location location = new Location()
            {
                File = breakpoint.standard.file,
                RedEngineLine = (uint)breakpoint.standard.line,

                Position = breakpoint.position,
            };

            return location;
        }

        public static Location CreateFromRedEngine( comm.ScriptBreakpointRelative breakpoint )
        {
            Location location = new Location()
            {
                File = breakpoint.standard.file,
                RedEngineLine = (uint)breakpoint.standard.line,

                Column = breakpoint.column,
                Length = breakpoint.length,
            };

            return location;
        }

        public static Location CreateFromVisualStudio( IDebugDocumentPosition2 source )
        {
            string document;
            source.GetFileName( out document );

            // Get the location in the document that the breakpoint is in.
            TEXT_POSITION[] startPosition = new TEXT_POSITION[ 1 ];
            TEXT_POSITION[] endPosition = new TEXT_POSITION[ 1 ];
            source.GetRange( startPosition, endPosition );

            Location location = new Location()
            {
                File = document,
                Column = startPosition[ 0 ].dwColumn,
                VisualStudioLine = startPosition[ 0 ].dwLine
            };

            IDebugDocumentPositionOffset2 offset = source as IDebugDocumentPositionOffset2;

            if ( offset != null )
            {
                uint position = 0, unused = 0;
                offset.GetRange( ref position, ref unused );

                location.Position = position;
            }

            return location;
        }

        #endregion Public Functions
    }
}
